﻿// TestPCIeDriver.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <Windows.h>
#include <setupapi.h>
#include <initguid.h>
#include <cfgmgr32.h>
#include <stdio.h>

#pragma execution_character_set("utf-8")
#pragma comment(lib, "SetupAPI.lib")	// 显式链接库

#define PCIE_DEVICE_TYPE					0x7014
#define USER_CTL(Index)						CTL_CODE(PCIE_DEVICE_TYPE, (Index), METHOD_BUFFERED, FILE_ANY_ACCESS)

#define MMIO_CTL_TEST						(USER_CTL(0x01))
#define MMIO_CTL_RD_REG						(USER_CTL(0x02))
#define MMIO_CTL_WR_REG						(USER_CTL(0x03))
#define MMIO_CTL_MAP_TO_USER				(USER_CTL(0x04))
#define MMIO_CTL_UNMAP_FROM_USER			(USER_CTL(0x05))
#define IRQ_CTL_GIVE_EVENT_HANDLE			(USER_CTL(0x06))
#define IRQ_CTL_RELEASE_OBJ					(USER_CTL(0x07))
#define PWR_CTL_WAKEUP						(USER_CTL(0x08))
#define PWR_CTL_IDLE						(USER_CTL(0x09))

#define PAGE_SIZE							4096

DEFINE_GUID(GUID_DEVINTERFACE_PCIeDriver,
	0xc13612ef, 0xc592, 0x46a0, 0xbd, 0x9c, 0xd2, 0x42, 0x86, 0x10, 0x36, 0x02);
// {c13612ef-c592-46a0-bd9c-d24286103602}

//LPCWSTR g_DevicePath = L"\\\\.\\Global\\GLOBALROOT\\Device\\PCIeDevice";
LPCWSTR g_DevicePath = L"\\\\.\\PCIeDevice";


/*
 * 槽位对应BDF
 *		槽位2： bus 12, dev 0, func 0
 *		槽位3： bus 9, dev 0, func 0
 *		槽位4： bus 3, dev 0, func 0
 *		槽位5： bus 4, dev 0, func 0
 *		槽位6： bus 5, dev 0, func 0
 *		槽位7： bus 8, dev 0, func 0
 * 槽位对应InstanceID后面的位置信息字符串
 *		槽位2： 4&13428119&0&00E8
 *		槽位3： 4&30BC0AB3&0&0009
 *		槽位4： 6&2E84EF4F&0&00080008
 *		槽位5： 6&AF58FF0&0&00100008
 *		槽位6： 6&1B858B61&0&00180008
 *		槽位7： 6&B9F82EC&0&00500008
*/


typedef struct _MMIO_ACCESS
{
	UINT32 Zone;			// BAR空间的区域：0,通道配置；2,DMA操作的读写的区域；4,普通的读写IO通道的区域
	UINT32 RegisterOffset;
	UINT32 RegisterValue;
} MMIO_ACCESS, * PMMIO_ACCESS;

typedef struct _MAP_MMIO_INFO
{
	UINT32 Zone;			// BAR空间的区域：0,通道配置；2,DMA操作的读写的区域；4,普通的读写IO通道的区域
	UINT32 MapMmioOffset;
	UINT32 MapMmioLength;
} MAP_MMIO_INFO, * PMAP_MMIO_INFO;


/* 这里加volatile，传入DeviceIoControl函数时会报错，改成驱动程序里，在MmMapIoSpace时加volatile
	*  以及应用程序在后面对mappedMmioBase强制类型转换时加上
	*/
	//volatile PVOID pMapMmioBase_Bar0 = 0;
PVOID pMapMmioBase_Bar0 = 0;
PVOID pMapMmioBase_Bar2 = 0;


// 通过设备实例ID(InstanceID)识别
BOOL FindDeviceByInstanceId(PCWSTR targetInstanceId, PWSTR devicePath, DWORD pathSize)
{
	HDEVINFO hDevInfo;
	SP_DEVICE_INTERFACE_DATA deviceInterfaceData = { 0 };
	SP_DEVINFO_DATA devInfoData = { 0 };
	DWORD requiredSize = 0;
	BOOL found = FALSE;

	// 获取设备信息集句柄(HDEVINFO)
	hDevInfo = SetupDiGetClassDevs(
		&GUID_DEVINTERFACE_PCIeDriver,			// 设备类GUID，NULL是枚举所有设备类
		NULL,									// 枚举器，按总线类型筛选设备。 L"PCI"、L"USB" 等
		NULL,									// 父窗口句柄，通常为NULL
		DIGCF_PRESENT | DIGCF_DEVICEINTERFACE 	// 控制标志。 DIGCF_PRESENT： 只枚举当前存在的设备  DIGCF_DEVICEINTERFACE：枚举设备接口(常用)  DIGCF_ALLCLASSES：枚举所以设备类  DIGCF_DEFAULT：只枚举默认设备
	);
	if (hDevInfo == INVALID_HANDLE_VALUE) 
	{
		printf("SetupDiGetClassDevs error: %d\n", GetLastError());
		return false;
	}

	deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
	devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);

	int i = 0;
	bool retInsId;
	bool ret = true;
	while(ret)
	{		
		// 从设备信息集中获取 SP_DEVINFO_DATA 设备信息
		ret = SetupDiEnumDeviceInfo(
			hDevInfo,
			i,
			&devInfoData
		);
		if (!ret) 
		{
			printf("SetupDiEnumDeviceInfo error. \n");
			return false;
		}

		// 获取设备实例 ID
		WCHAR instanceId[256] = { 0 };
		retInsId = SetupDiGetDeviceInstanceId(
			hDevInfo,							// 设备信息集句柄
			&devInfoData,						// 设备信息数据，指向设备信息数据的指针
			instanceId,							// 输出缓冲区，接收实例ID
			sizeof(instanceId) / sizeof(WCHAR),	// 缓冲区大小，字符数
			NULL								// 所需缓冲区大小，可选
		);

		if (retInsId)
		{
			wprintf(L"InInstanceID: %ls\n", instanceId);

			if (wcscmp(instanceId, targetInstanceId) == 0)
			{
				// 枚举设备信息集中的设备接口
				ret = SetupDiEnumDeviceInterfaces(
					hDevInfo,						// 设备信息集句柄
					NULL,							// 设备信息数据，通常为NULL
					&GUID_DEVINTERFACE_PCIeDriver,  // 设备接口类GUID
					i,								// 设备索引，从0开始
					&deviceInterfaceData			// 此函数输出的设备接口数据
				);
					
				// 获取设备路径
				PSP_DEVICE_INTERFACE_DETAIL_DATA pDetail = NULL;
				SetupDiGetDeviceInterfaceDetail(hDevInfo, &deviceInterfaceData, NULL, 0, &requiredSize, NULL);			// 第1次调用获取缓冲区的大小
				pDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(requiredSize);
				pDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
				
				if (SetupDiGetDeviceInterfaceDetail(hDevInfo, &deviceInterfaceData, pDetail, requiredSize, NULL, NULL))	// 第2次调用获取设备路径
				{
					wcscpy_s(devicePath, pathSize, pDetail->DevicePath);
					found = TRUE;
				}

				free(pDetail);
				break;
			}
		}

		i++;
	}

	SetupDiDestroyDeviceInfoList(hDevInfo);
	return found;
}

// 通过设备位置识别
BOOL FindDeviceByLocation(PCWSTR targetLocation, PWSTR devicePath, DWORD pathSize)
{
	HDEVINFO hDevInfo;
	SP_DEVICE_INTERFACE_DATA deviceInterfaceData = { 0 };
	SP_DEVINFO_DATA devInfoData = { 0 };
	DWORD requiredSize = 0;
	BOOL found = FALSE;

	hDevInfo = SetupDiGetClassDevs(
		&GUID_DEVINTERFACE_PCIeDriver,
		NULL,
		NULL,
		DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);

	deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
	devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);

	int i = 0;
	bool retInsLocation;
	bool ret = true;

	while (ret)
	{
		// 从设备信息集中获取 SP_DEVINFO_DATA 设备信息
		ret = SetupDiEnumDeviceInfo(
			hDevInfo,
			i,
			&devInfoData
		);
		if (!ret)
		{
			printf("SetupDiEnumDeviceInfo error. \n");
			return false;
		}
		
		// 获取设备位置信息
		WCHAR locationInfo[256] = { 0 };
		retInsLocation = SetupDiGetDeviceRegistryProperty(
			hDevInfo,					// 设备信息集句柄
			&devInfoData,				// 设备信息数据
			SPDRP_LOCATION_INFORMATION,	// 要检索的属性
			NULL,						// 属性数据类型（可选）
			(PBYTE)locationInfo,		// 输出缓冲区
			sizeof(locationInfo),		// 缓冲区大小
			NULL						// 所需缓冲区大小（可选）
		);

		if (retInsLocation)
		{
			wprintf(L"locationInfo: %ls\n", locationInfo);

			if (wcscmp(locationInfo, targetLocation) == 0)
			{
				// 枚举设备信息集中的设备接口
				SetupDiEnumDeviceInterfaces(
					hDevInfo,						// 设备信息集句柄
					NULL,							// 设备信息数据，通常为NULL
					&GUID_DEVINTERFACE_PCIeDriver,  // 设备接口类GUID
					i,								// 设备索引，从0开始
					&deviceInterfaceData			// 此函数输出的设备接口数据
				);
				
				// 获取设备路径
				PSP_DEVICE_INTERFACE_DETAIL_DATA pDetail = NULL;
				SetupDiGetDeviceInterfaceDetail(hDevInfo, &deviceInterfaceData, NULL, 0, &requiredSize, NULL);			// 第1次调用获取缓冲区的大小
				pDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(requiredSize);
				pDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

				if (SetupDiGetDeviceInterfaceDetail(hDevInfo, &deviceInterfaceData, pDetail, requiredSize, NULL, NULL))	// 第2次调用获取设备路径
				{
					wcscpy_s(devicePath, pathSize, pDetail->DevicePath);
					found = TRUE;
				}

				free(pDetail);
				break;
			}
		}
	}

	SetupDiDestroyDeviceInfoList(hDevInfo);
	return found;
}

// 测试与内核的控制链接
bool testMMIOCtl(HANDLE deviceHandle)
{
	ULONG bytesReturned = 0;
	
	if (!DeviceIoControl(
		deviceHandle,
		MMIO_CTL_TEST,
		NULL,
		0,
		NULL,
		0,
		&bytesReturned,
		NULL))
	{
		printf("MY_IOCTL_TEST failed, error = %u", GetLastError());
		return false;
	}
	printf("Handle DeviceIoControl CTL_CODE: MY_IOCTL_TEST successfully\n");

	return true;
}

// 测试控制板卡的LED灯
bool testLightLED(HANDLE deviceHandle)
{
	MMIO_ACCESS regAccess;
	ULONG bytesReturned = 0;
	
	regAccess.Zone = 0;
	regAccess.RegisterOffset = 0x14;
	regAccess.RegisterValue = 0x1;
	if (!DeviceIoControl(
		deviceHandle,
		MMIO_CTL_WR_REG,
		&regAccess,
		sizeof(regAccess),
		NULL,
		0,
		&bytesReturned,
		NULL))
	{
		printf("MY_IOCTL_WR_REG failed, error = %u", GetLastError());
		return false;
	}
	printf("write LED Control register: %x, value: %x\n", regAccess.RegisterOffset, regAccess.RegisterValue);

	return true;
}


// 映射MMIO到用户态地址空间
bool MapMMIOToUserBase(HANDLE deviceHandle)
{
	ULONG bytesReturned = 0;
	
	// 映射配置空间BAR0
	MAP_MMIO_INFO mapInfo1;
	mapInfo1.Zone = 0;
	mapInfo1.MapMmioOffset = 0;
	mapInfo1.MapMmioLength = PAGE_SIZE;
	if (!DeviceIoControl(
		deviceHandle,
		MMIO_CTL_MAP_TO_USER,
		&mapInfo1,
		sizeof(mapInfo1),
		&pMapMmioBase_Bar0,
		sizeof(pMapMmioBase_Bar0),
		&bytesReturned,
		NULL))
	{
		printf("MY_IOCTL_MAP_MMIO_TO_USER failed, error=%u", GetLastError());
		return false;
	}

	// 映射空间BAR2
	MAP_MMIO_INFO mapInfo2;
	mapInfo2.Zone = 4;
	mapInfo2.MapMmioOffset = 0;
	mapInfo2.MapMmioLength = PAGE_SIZE * 17;
	if (!DeviceIoControl(
		deviceHandle,
		MMIO_CTL_MAP_TO_USER,
		&mapInfo2,
		sizeof(mapInfo2),
		&pMapMmioBase_Bar2,
		sizeof(pMapMmioBase_Bar2),
		&bytesReturned,
		NULL))
	{
		printf("MY_IOCTL_MAP_MMIO_TO_USER failed, error=%u", GetLastError());
		return false;
	}

	return true;
}

// 解除MMIO到用户态地址空间的映射
bool UnmapMMIOToUserBase(HANDLE deviceHandle)
{
	ULONG bytesReturned = 0;

	// 解除空间BAR0映射
	if (!DeviceIoControl(
		deviceHandle,
		MMIO_CTL_UNMAP_FROM_USER,
		&pMapMmioBase_Bar0,
		sizeof(pMapMmioBase_Bar0),
		NULL,
		0,
		&bytesReturned,
		NULL))
	{
		printf("MY_IOCTL_UNMAP_MMIO_FROM_USER failed, error = %u", GetLastError());
		return false;
	}

	// 解除空间BAR2映射
	if (!DeviceIoControl(
		deviceHandle,
		MMIO_CTL_UNMAP_FROM_USER,
		&pMapMmioBase_Bar2,
		sizeof(pMapMmioBase_Bar2),
		NULL,
		0,
		&bytesReturned,
		NULL))
	{
		printf("MY_IOCTL_UNMAP_MMIO_FROM_USER failed, error = %u", GetLastError());
		return false;
	}

	return true;
}


// 初始化DIO通道配置，使用每次进入内核态读写MMIO的方式
bool initChannelConf_DIO(HANDLE deviceHandle, int direct)
{
	MMIO_ACCESS regAccess;
	MMIO_ACCESS regOut;
	ULONG bytesReturned = 0;

	// 写阈值电压，0和1的界限
	regAccess.Zone = 0;
	regAccess.RegisterOffset = 0x28;
	regAccess.RegisterValue = 0x0C0C0C0C;   // 0x0C0C0C0C
	if (!DeviceIoControl(
		deviceHandle,
		MMIO_CTL_WR_REG,
		&regAccess,
		sizeof(regAccess),
		NULL,
		0,
		&bytesReturned,
		NULL))
	{
		printf("MY_IOCTL_WR_REG failed, error = %u", GetLastError());
		return false;
	}
	printf("write DIO threshold register: %x, value: %x\n",regAccess.RegisterOffset, regAccess.RegisterValue);
	
	// 写参考电压
	regAccess.Zone = 0;
	regAccess.RegisterOffset = 0x2C;
	regAccess.RegisterValue = 0x00000000;   // 0是参考电压1，1是参考电压2，这里17-32路设成参考电压1，1-16路设成参考电压2
	if (!DeviceIoControl(
		deviceHandle,
		MMIO_CTL_WR_REG,
		&regAccess,
		sizeof(regAccess),
		NULL,
		0,
		&bytesReturned,
		NULL))
	{
		printf("MY_IOCTL_WR_REG failed, error = %u", GetLastError());
		return false;
	}
	printf("write DIO refVolt register: %x, value: %x\n", regAccess.RegisterOffset, regAccess.RegisterValue);

	// 写1-16通道的模式
	regAccess.Zone = 0;
	regAccess.RegisterOffset = 0x30;
	regAccess.RegisterValue = 0x00000000;	// 16个通道都是输入 
	if (!DeviceIoControl(
		deviceHandle,
		MMIO_CTL_WR_REG,
		&regAccess,
		sizeof(regAccess),
		NULL,
		0,
		&bytesReturned,
		NULL))
	{
		printf("MY_IOCTL_WR_REG failed, error = %u", GetLastError());
		return false;
	}
	printf("write DIO 1-16 mode register: %x, value: %x\n", regAccess.RegisterOffset, regAccess.RegisterValue);

	// 写17-32通道的模式
	regAccess.Zone = 0;
	regAccess.RegisterOffset = 0x34;
	regAccess.RegisterValue = 0xFFFFFFFF;	// 16个通道全部是推挽输出
	if (!DeviceIoControl(
		deviceHandle,
		MMIO_CTL_WR_REG,
		&regAccess,
		sizeof(regAccess),
		NULL,
		0,
		&bytesReturned,
		NULL))
	{
		printf("MY_IOCTL_WR_REG failed, error = %u", GetLastError());
		return false;
	}
	printf("write DIO 17-32 mode register: %x, value: %x\n", regAccess.RegisterOffset, regAccess.RegisterValue);

	// 写1-8通道的工作模式
	regAccess.Zone = 4;
	regAccess.RegisterOffset = 0x10104;
	regAccess.RegisterValue = 0x00000000;  // 8个通道都是数字输入
	if (!DeviceIoControl(
		deviceHandle,
		MMIO_CTL_WR_REG,
		&regAccess,
		sizeof(regAccess),
		NULL,
		0,
		&bytesReturned,
		NULL))
	{
		printf("MY_IOCTL_WR_REG failed, error = %u", GetLastError());
		return false;
	}
	printf("write DIO 1-8 function register: %x, value: %x\n", regAccess.RegisterOffset, regAccess.RegisterValue);

	// 写9-16通道的工作模式
	regAccess.Zone = 4;
	regAccess.RegisterOffset = 0x10108;
	regAccess.RegisterValue = 0x00000000;	// 8个通道都是数字输入 
	if (!DeviceIoControl(
		deviceHandle,
		MMIO_CTL_WR_REG,
		&regAccess,
		sizeof(regAccess),
		NULL,
		0,
		&bytesReturned,
		NULL))
	{
		printf("MY_IOCTL_WR_REG failed, error = %u", GetLastError());
		return false;
	}
	printf("write DIO 9-16 function register: %x, value: %x\n", regAccess.RegisterOffset, regAccess.RegisterValue);

	// 写17-24通道的工作模式
	regAccess.Zone = 4;
	regAccess.RegisterOffset = 0x1010C;
	regAccess.RegisterValue = 0x88888888;	// 8个通道都是数字输出
	if (!DeviceIoControl(
		deviceHandle,
		MMIO_CTL_WR_REG,
		&regAccess,
		sizeof(regAccess),
		NULL,
		0,
		&bytesReturned,
		NULL))
	{
		printf("MY_IOCTL_WR_REG failed, error = %u", GetLastError());
		return false;
	}
	printf("write DIO 17-24 function register: %x, value: %x\n", regAccess.RegisterOffset, regAccess.RegisterValue);

	// 写25-32通道的工作模式
	regAccess.Zone = 4;
	regAccess.RegisterOffset = 0x10110;
	regAccess.RegisterValue = 0x88888888;	// 8个通道都是数字输出
	if (!DeviceIoControl(
		deviceHandle,
		MMIO_CTL_WR_REG,
		&regAccess,
		sizeof(regAccess),
		NULL,
		0,
		&bytesReturned,
		NULL))
	{
		printf("MY_IOCTL_WR_REG failed, error = %u", GetLastError());
		return false;
	}
	printf("write DIO 25-32 function register: %x, value: %x\n", regAccess.RegisterOffset, regAccess.RegisterValue);

	// 1-16通道配置数字输入模式
	//for (int i = 0; i < 16; i++)
	//{
	//	regAccess.Zone = 4;
	//	regAccess.RegisterOffset = 0x10118 + 8 * i;
	//	regAccess.RegisterValue = 0x0;			// 设置模式为数字输出模式，此模式下其它的位不需要配置
	//	if (!DeviceIoControl(
	//		deviceHandle,
	//		MMIO_CTL_WR_REG,
	//		&regAccess,
	//		sizeof(regAccess),
	//		NULL,
	//		0,
	//		&bytesReturned,
	//		NULL))
	//	{
	//		printf("MY_IOCTL_WR_REG failed, error = %u", GetLastError());
	//		return false;
	//	}
	//	printf("write register: %x, value: %x\n", regAccess.RegisterOffset, regAccess.RegisterValue);
	//}

	// 17-32通道配置数字输出模式
	for (int i=0; i<16; i++) 
	{
		regAccess.Zone = 4;
		regAccess.RegisterOffset = 0x10198 + 8 * i;
		regAccess.RegisterValue = 0x8;			// 设置模式为数字输出模式，此模式下其它的位不需要配置
		if (!DeviceIoControl(
			deviceHandle,
			MMIO_CTL_WR_REG,
			&regAccess,
			sizeof(regAccess),
			NULL,
			0,
			&bytesReturned,
			NULL))
		{
			printf("MY_IOCTL_WR_REG failed, error = %u", GetLastError());
			return false;
		}
		printf("write register: %x, value: %x\n", regAccess.RegisterOffset, regAccess.RegisterValue);
	}



	// 读取写入的阈值电压
	regAccess.Zone = 0;
	regAccess.RegisterOffset = 0x28;
	if (!DeviceIoControl(
		deviceHandle,
		MMIO_CTL_RD_REG,
		&regAccess,
		sizeof(regAccess),
		&regOut,
		sizeof(regOut),
		&bytesReturned,
		NULL))
	{
		printf("MY_IOCTL_RD_REG failed, error = %u", GetLastError());
		return false;
	}
	printf("read DIO threshold register: %x, value: %x\n", regOut.RegisterOffset, regOut.RegisterValue);

	// 读取写入的参考电压值
	regAccess.Zone = 0;
	regAccess.RegisterOffset = 0x2C;
	if (!DeviceIoControl(
		deviceHandle,
		MMIO_CTL_RD_REG,
		&regAccess,
		sizeof(regAccess),
		&regOut,
		sizeof(regOut),
		&bytesReturned,
		NULL))
	{
		printf("MY_IOCTL_RD_REG failed, error = %u", GetLastError());
		return false;
	}
	printf("read DIO refVolt register: %x, value: %x\n", regOut.RegisterOffset, regOut.RegisterValue);

	// 读取写入的1-16通道的模式
	regAccess.Zone = 0;
	regAccess.RegisterOffset = 0x30;
	if (!DeviceIoControl(
		deviceHandle,
		MMIO_CTL_RD_REG,
		&regAccess,
		sizeof(regAccess),
		&regOut,
		sizeof(regOut),
		&bytesReturned,
		NULL))
	{
		printf("MY_IOCTL_RD_REG failed, error = %u", GetLastError());
		return false;
	}
	printf("read DIO 1-16 mode register: %x, value: %x\n", regOut.RegisterOffset, regOut.RegisterValue);

	// 读取写入的17-32通道的模式
	regAccess.Zone = 0;
	regAccess.RegisterOffset = 0x34;
	if (!DeviceIoControl(
		deviceHandle,
		MMIO_CTL_RD_REG,
		&regAccess,
		sizeof(regAccess),
		&regOut,
		sizeof(regOut),
		&bytesReturned,
		NULL))
	{
		printf("MY_IOCTL_RD_REG failed, error = %u", GetLastError());
		return false;
	}
	printf("read DIO 17-32 mode register: %x, value: %x\n", regOut.RegisterOffset, regOut.RegisterValue);

	// 读取写入的1-8通道的工作模式
	regAccess.Zone = 4;
	regAccess.RegisterOffset = 0x10104;
	if (!DeviceIoControl(
		deviceHandle,
		MMIO_CTL_RD_REG,
		&regAccess,
		sizeof(regAccess),
		&regOut,
		sizeof(regOut),
		&bytesReturned,
		NULL))
	{
		printf("MY_IOCTL_RD_REG failed, error = %u", GetLastError());
		return false;
	}
	printf("read DIO 1-8 function register: %x, value: %x\n", regOut.RegisterOffset, regOut.RegisterValue);

	// 读取写入的9-16通道的工作模式
	regAccess.Zone = 4;
	regAccess.RegisterOffset = 0x10108;
	if (!DeviceIoControl(
		deviceHandle,
		MMIO_CTL_RD_REG,
		&regAccess,
		sizeof(regAccess),
		&regOut,
		sizeof(regOut),
		&bytesReturned,
		NULL))
	{
		printf("MY_IOCTL_RD_REG failed, error = %u", GetLastError());
		return false;
	}
	printf("read DIO 9-16 function register: %x, value: %x\n", regOut.RegisterOffset, regOut.RegisterValue);

	// 读取写入的17-24通道的工作模式
	regAccess.Zone = 4;
	regAccess.RegisterOffset = 0x1010C;
	if (!DeviceIoControl(
		deviceHandle,
		MMIO_CTL_RD_REG,
		&regAccess,
		sizeof(regAccess),
		&regOut,
		sizeof(regOut),
		&bytesReturned,
		NULL))
	{
		printf("MY_IOCTL_RD_REG failed, error = %u", GetLastError());
		return false;
	}
	printf("read DIO 17-24 function register: %x, value: %x\n", regOut.RegisterOffset, regOut.RegisterValue);

	// 读取写入的25-32通道的工作模式
	regAccess.Zone = 4;
	regAccess.RegisterOffset = 0x10110;
	if (!DeviceIoControl(
		deviceHandle,
		MMIO_CTL_RD_REG,
		&regAccess,
		sizeof(regAccess),
		&regOut,
		sizeof(regOut),
		&bytesReturned,
		NULL))
	{
		printf("MY_IOCTL_RD_REG failed, error = %u", GetLastError());
		return false;
	}
	printf("read DIO 25-32 function register: %x, value: %x\n", regOut.RegisterOffset, regOut.RegisterValue);
	
	return true;
}

// 初始化DIO通道配置方式2
bool initChannelConfEx_DIO(HANDLE deviceHandle, int direct)
{
	// 写阈值电压，0和1的界限
	UINT32 regAddrOffset = 0x28;
	UINT32 regValue = 0x0C0C0C0C;	// 2个芯片都使能，bit16和bit0
	*(PUINT32)((volatile UINT64)pMapMmioBase_Bar0 + regAddrOffset) = regValue;
	printf("write register: %x from user space MMIO, value: %x \n", regAddrOffset, regValue);

	// 写参考电压
	regAddrOffset = 0x2C;
	regValue = 0x00000000;   // 0是参考电压1，1是参考电压2，这里17-32路设成参考电压1，1-16路设成参考电压2
	*(PUINT32)((volatile UINT64)pMapMmioBase_Bar0 + regAddrOffset) = regValue;
	printf("write register: %x from user space MMIO, value: %x \n", regAddrOffset, regValue);

	// 写1-16通道的模式
	regAddrOffset = 0x30;
	regValue = 0x00000000;	// 16个通道都是输入
	*(PUINT32)((volatile UINT64)pMapMmioBase_Bar0 + regAddrOffset) = regValue;
	printf("write register: %x from user space MMIO, value: %x \n", regAddrOffset, regValue);

	// 写17-32通道的模式
	regAddrOffset = 0x34;
	regValue = 0xFFFFFFFF;	// 16个通道全部是推挽输出
	*(PUINT32)((volatile UINT64)pMapMmioBase_Bar0 + regAddrOffset) = regValue;
	printf("write register: %x from user space MMIO, value: %x \n", regAddrOffset, regValue);

	// 写1-8通道的工作模式
	regAddrOffset = 0x10104;
	regValue = 0x00000000;  // 8个通道都是数字输入
	*(PUINT32)((volatile UINT64)pMapMmioBase_Bar2 + regAddrOffset) = regValue;
	printf("write register: %x from user space MMIO, value: %x \n", regAddrOffset, regValue);

	// 写9-16通道的工作模式
	regAddrOffset = 0x10108;
	regValue = 0x00000000;  // 8个通道都是数字输入
	*(PUINT32)((volatile UINT64)pMapMmioBase_Bar2 + regAddrOffset) = regValue;
	printf("write register: %x from user space MMIO, value: %x \n", regAddrOffset, regValue);

	// 写17-24通道的工作模式
	regAddrOffset = 0x1010C;
	regValue = 0x88888888;  // 8个通道都是数字输出
	*(PUINT32)((volatile UINT64)pMapMmioBase_Bar2 + regAddrOffset) = regValue;
	printf("write register: %x from user space MMIO, value: %x \n", regAddrOffset, regValue);

	// 写25-32通道的工作模式
	regAddrOffset = 0x10110;
	regValue = 0x88888888;  // 8个通道都是数字输出
	*(PUINT32)((volatile UINT64)pMapMmioBase_Bar2 + regAddrOffset) = regValue;
	printf("write register: %x from user space MMIO, value: %x \n", regAddrOffset, regValue);

	// 1-16通道配置数字输入模式
	//for (int i = 0; i < 16; i++)
	//{
	//	regAddrOffset = 0x10118 + 8 * i;
	//	regValue = 0x0;		
	//	*(PUINT32)((volatile UINT64)pMapMmioBase_Bar2 + regAddrOffset) = regValue;
	//	printf("write register: %x from user space MMIO, value: %x \n", regAddrOffset, regValue);
	//}

	// 17-32通道配置数字输出模式
	for (int i = 0; i < 16; i++)
	{
		regAddrOffset = 0x10198 + 8 * i;
		regValue = 0x8;		
		*(PUINT32)((volatile UINT64)pMapMmioBase_Bar2 + regAddrOffset) = regValue;
		printf("write register: %x from user space MMIO, value: %x \n", regAddrOffset, regValue);
	}


	// 读取写入的阈值电压
	regAddrOffset = 0x28;
	regValue = *(PUINT32)((volatile UINT64)pMapMmioBase_Bar0 + regAddrOffset);
	printf("read register: %x from user space MMIO, value； %x\n", regAddrOffset, regValue);

	return true;
}

// 初始化ADC通道配置
bool initChannelConf_ADC(HANDLE deviceHandle)
{
	MMIO_ACCESS regAccess;
	ULONG bytesReturned = 0;
	
	// ADC配置使能
	regAccess.Zone = 0;
	regAccess.RegisterOffset = 0x20;
	regAccess.RegisterValue = 0x00010001;	// 2个芯片都使能，bit16和bit0
	if (!DeviceIoControl(
		deviceHandle,
		MMIO_CTL_WR_REG,
		&regAccess,
		sizeof(regAccess),
		NULL,
		0,
		&bytesReturned,
		NULL))
	{
		printf("MY_IOCTL_WR_REG failed, error = %u", GetLastError());
		return false;
	}
	printf("write ADC enable register: %x, value: %x\n", regAccess.RegisterOffset, regAccess.RegisterValue);

	// 配置ADC量程
	regAccess.Zone = 0;
	regAccess.RegisterOffset = 0x38;
	regAccess.RegisterValue = 0xFD7CFD7C;	// 2个芯片都使能，bit16和bit0
	if (!DeviceIoControl(
		deviceHandle,
		MMIO_CTL_WR_REG,
		&regAccess,
		sizeof(regAccess),
		NULL,
		0,
		&bytesReturned,
		NULL))
	{
		printf("MY_IOCTL_WR_REG failed, error = %u", GetLastError());
		return false;
	}
	printf("write ADC cfg register: %x, value: %x\n", regAccess.RegisterOffset, regAccess.RegisterValue);

	// ADC配置不使能
	regAccess.Zone = 0;
	regAccess.RegisterOffset = 0x20;
	regAccess.RegisterValue = 0x00000000;	
	if (!DeviceIoControl(
		deviceHandle,
		MMIO_CTL_WR_REG,
		&regAccess,
		sizeof(regAccess),
		NULL,
		0,
		&bytesReturned,
		NULL))
	{
		printf("MY_IOCTL_WR_REG failed, error = %u", GetLastError());
		return false;
	}
	printf("write ADC enable register: %x, value: %x\n", regAccess.RegisterOffset, regAccess.RegisterValue);


	// 读取写入的量程配置寄存器
	regAccess.Zone = 0;
	regAccess.RegisterOffset = 0x38;
	if (!DeviceIoControl(
		deviceHandle,
		MMIO_CTL_RD_REG,
		&regAccess,
		sizeof(regAccess),
		&regAccess,
		sizeof(regAccess),
		&bytesReturned,
		NULL))
	{
		printf("MY_IOCTL_RD_REG failed, error = %u", GetLastError());
		return false;
	}
	printf("read ADC cfg register: %x, value: %x\n", regAccess.RegisterOffset, regAccess.RegisterValue);


	return true;
}

// 初始化ADC通道配置方式2
bool initChannelConfEx_ADC(HANDLE deviceHandle)
{
	// ADC配置使能
	UINT32 regAddrOffset = 0x20;
	UINT32 regValue = 0x00010001;	// 2个芯片都使能，bit16和bit0
	*(PUINT32)((volatile UINT64)pMapMmioBase_Bar0 + regAddrOffset) = regValue;
	printf("write register: %x from user space MMIO, value: %x \n", regAddrOffset, regValue);

	// 配置ADC量程
	regAddrOffset = 0x38;
	regValue = 0xFD7CFD7C;	
	*(PUINT32)((volatile UINT64)pMapMmioBase_Bar0 + regAddrOffset) = regValue;
	printf("write register: %x from user space MMIO, value: %x \n", regAddrOffset, regValue);

	// ADC配置不使能
	regAddrOffset = 0x20;
	regValue = 0x00000000;
	*(PUINT32)((volatile UINT64)pMapMmioBase_Bar0 + regAddrOffset) = regValue;
	printf("write register: %x from user space MMIO, value: %x \n", regAddrOffset, regValue);

	// 读取写入的量程配置寄存器
	regAddrOffset = 0x38;
	regValue = *(PUINT32)((volatile UINT64)pMapMmioBase_Bar0 + regAddrOffset);
	printf("read register: %x from user space MMIO, value； %x\n", regAddrOffset, regValue);

	return true;
}

// 初始化DAC通道配置
bool initChannelConf_DAC(HANDLE deviceHandle)
{
	MMIO_ACCESS regAccess;
	ULONG bytesReturned = 0;

	// DAC 配置使能，高8位是密码
	unsigned int cfgEn[5] = { 0x21000001, 0x21000010, 0x21000100, 0x21001000, 0x21010000 };
	// DAC 芯片量程
	unsigned int outputRange[5] = { 0x000A6666, 0x000A6666, 0x000A6666, 0x000A6666, 0x000A6666 };

	for (int i=0; i<5; i++) 
	{
		// 先使能
		regAccess.Zone = 0;
		regAccess.RegisterOffset = 0x24;
		regAccess.RegisterValue = cfgEn[i];	// 2个芯片都使能，bit16和bit0
		if (!DeviceIoControl(
			deviceHandle,
			MMIO_CTL_WR_REG,
			&regAccess,
			sizeof(regAccess),
			NULL,
			0,
			&bytesReturned,
			NULL))
		{
			printf("MY_IOCTL_WR_REG failed, error = %u", GetLastError());
			return false;
		}
		printf("write DAC enable register: %x, value: %x\n", regAccess.RegisterOffset, regAccess.RegisterValue);

		// 设置量程
		regAccess.Zone = 0;
		regAccess.RegisterOffset = 0x3C + 8 * i;
		regAccess.RegisterValue = outputRange[i];
		if (!DeviceIoControl(
			deviceHandle,
			MMIO_CTL_WR_REG,
			&regAccess,
			sizeof(regAccess),
			NULL,
			0,
			&bytesReturned,
			NULL))
		{
			printf("MY_IOCTL_WR_REG failed, error = %u", GetLastError());
			return false;
		}
		printf("write DAC set output range register: %x, value: %x\n", regAccess.RegisterOffset, regAccess.RegisterValue);

		// 延迟5ms
		Sleep(5);
	}

	// 最后写一次不使能
	regAccess.Zone = 0;
	regAccess.RegisterOffset = 0x24;
	regAccess.RegisterValue = 0;	// 2个芯片都不使能
	if (!DeviceIoControl(
		deviceHandle,
		MMIO_CTL_WR_REG,
		&regAccess,
		sizeof(regAccess),
		NULL,
		0,
		&bytesReturned,
		NULL))
	{
		printf("MY_IOCTL_WR_REG failed, error = %u", GetLastError());
		return false;
	}
	printf("write ADC enable register: %x, value: %x\n", regAccess.RegisterOffset, regAccess.RegisterValue);


	// 读取5个DAC输出量程配置的寄存器
	for (int i = 0; i < 5; i++)
	{
		regAccess.Zone = 0;
		regAccess.RegisterOffset = 0x3C + 8 * i;
		if (!DeviceIoControl(
			deviceHandle,
			MMIO_CTL_RD_REG,
			&regAccess,
			sizeof(regAccess),
			&regAccess,
			sizeof(regAccess),
			&bytesReturned,
			NULL))
		{
			printf("MY_IOCTL_RD_REG failed, error = %u", GetLastError());
			return false;
		}
		printf("read DAC output range register: %x, value: %x\n", regAccess.RegisterOffset, regAccess.RegisterValue);
	}

	return true;
}

// 初始化DAC通道配置方式2
bool initChannelConfEx_DAC(HANDLE deviceHandle)
{
	// DAC 配置使能，高8位是密码
	unsigned int cfgEn[5] = { 0x21000001, 0x21000010, 0x21000100, 0x21001000, 0x21010000 };
	// DAC 芯片量程
	unsigned int outputRange[5] = { 0x000A6666, 0x000A6666, 0x000A6666, 0x000A6666, 0x000A6666 };

	for (int i = 0; i < 5; i++)
	{
		// 先使能
		UINT32 regAddrOffset1 = 0x24;
		UINT32 regValue1 = cfgEn[i];
		*(PUINT32)((volatile UINT64)pMapMmioBase_Bar0 + regAddrOffset1) = regValue1;
		printf("write register: %x from user space MMIO, value: %x \n", regAddrOffset1, regValue1);

		// 设置量程
		UINT32 regAddrOffset2 = 0x3C + 8 * i;
		UINT32 regValue2 = outputRange[i];
		*(PUINT32)((volatile UINT64)pMapMmioBase_Bar0 + regAddrOffset2) = regValue2;
		printf("write register: %x from user space MMIO, value: %x \n", regAddrOffset2, regValue2);

		// 延迟5ms
		Sleep(5);
	}

	// 最后写一次不使能
	UINT32 regAddrOffset = 0x24;
	UINT32 regValue = 0;
	*(PUINT32)((volatile UINT64)pMapMmioBase_Bar0 + regAddrOffset) = regValue;
	printf("write register: %x from user space MMIO, value: %x \n", regAddrOffset, regValue);
	
	// 读取5个DAC输出量程配置的寄存器
	for (int i = 0; i < 5; i++)
	{
		UINT32 regAddrOffSet = 0x3C + 8 * i;
		UINT32 regValue = *(PUINT32)((volatile UINT64)pMapMmioBase_Bar0 + regAddrOffSet);
		printf("read register: %x from user space MMIO, value； %x\n", regAddrOffSet, regValue);
	}

	return true;
}


// 读取DIO的输入
bool ReadDIOInputValue(HANDLE deviceHandle, UINT32 &result)
{
	MMIO_ACCESS regAccess;
	MMIO_ACCESS regOut;
	ULONG bytesReturned = 0;

	regAccess.Zone = 4;
	regAccess.RegisterOffset = 0x10318;
	if (!DeviceIoControl(
		deviceHandle,
		MMIO_CTL_RD_REG,
		&regAccess,
		sizeof(regAccess),
		&regOut,
		sizeof(regOut),
		&bytesReturned,
		NULL))
	{
		printf("MY_IOCTL_RD_REG failed, error = %u", GetLastError());
		return false;
	}
	printf("read DIO input value register: %x, value: %x\n", regOut.RegisterOffset, regOut.RegisterValue);

	return true;
}

// 读取DIO是输入，方式2
bool ReadDIOInputValueEx(HANDLE deviceHandle, UINT32& result)
{
	UINT32 regAddrOffSet = 0x10318;
	UINT32 regValue = *(PUINT32)((volatile UINT64)pMapMmioBase_Bar2 + regAddrOffSet);
	printf("read register: %x from user space MMIO, value； %x\n", regAddrOffSet, regValue);

	return true;
}

// 设置DIO的输出
bool setDIOOutputValue(HANDLE deviceHandle, ULONG value)
{
	MMIO_ACCESS regAccess;
	ULONG bytesReturned = 0;
	
	regAccess.Zone = 4;
	regAccess.RegisterOffset = 0x10114;
	regAccess.RegisterValue = value;	// 8个通道都是数字输出
	if (!DeviceIoControl(
		deviceHandle,
		MMIO_CTL_WR_REG,
		&regAccess,
		sizeof(regAccess),
		NULL,
		0,
		&bytesReturned,
		NULL))
	{
		printf("MY_IOCTL_WR_REG failed, error = %u", GetLastError());
		return false;
	}
	printf("write DIO output value function register: %x, value: %x\n", regAccess.RegisterOffset, regAccess.RegisterValue);

	return true;
}

// 设置DIO的输出，方式2
bool setDIOOutputValueEx(HANDLE deviceHandle, ULONG value)
{
	UINT32 regAddrOffset = 0x10114;
	*(PUINT32)((volatile UINT64)pMapMmioBase_Bar2 + regAddrOffset) = value;
	printf("write register: %x from user space MMIO, value: %x \n", regAddrOffset, value);

	return true;
}

// 读取ADC的输入值
bool readADCValue(HANDLE deviceHandle, int channel, UINT16 &result)
{
	MMIO_ACCESS regAccess;
	MMIO_ACCESS regOut;
	ULONG bytesReturned = 0;
	UINT32 RegOffset;
	UINT16 nResult;
	
	if ((channel >= 1) && (channel <= 8))
	{
		RegOffset = 0x1000 + (channel / 2) * 4;
	} else if ((channel >= 9) && (channel <= 16)) {
		RegOffset = 0x2000 + ((channel - 8) / 2) * 4;
	} else {
		printf("readADCValue, channel: %d, channel error! ", channel);
		return false;
	}

	regAccess.Zone = 4;
	regAccess.RegisterOffset = RegOffset;
	if (!DeviceIoControl(
		deviceHandle,
		MMIO_CTL_RD_REG,
		&regAccess,
		sizeof(regAccess),
		&regOut,
		sizeof(regOut),
		&bytesReturned,
		NULL))
	{
		printf("MY_IOCTL_RD_REG failed, error = %u", GetLastError());
		return false;
	}

	if (channel % 2)
	{
		nResult = (regOut.RegisterValue >> 16) & 0xFFFF;
	} else {
		nResult = regOut.RegisterValue & 0xFFFF;
	}

	printf("read ADC input value channel: %d, register: %x, value: %x\n", channel, regOut.RegisterOffset, nResult);

	return true;
}

// 读取ADC的输入值，方式2
bool readADCValueEx(HANDLE deviceHandle, int channel, UINT16& result)
{
	UINT32 regAddrOffSet;
	
	if ((channel >= 1) && (channel <= 8))
	{
		regAddrOffSet = 0x1000 + (channel / 2) * 4;
	}
	else if ((channel >= 9) && (channel <= 16)) {
		regAddrOffSet = 0x2000 + ((channel - 8) / 2) * 4;
	}
	else {
		printf("readADCValue, channel: %d, channel error! ", channel);
		return false;
	}
	
	UINT32 regValue = *(PUINT32)((volatile UINT64)pMapMmioBase_Bar2 + regAddrOffSet);
	printf("read register: %x from user space MMIO, value； %x\n", regAddrOffSet, regValue);

	if (channel % 2)
	{
		result = (regValue >> 16) & 0xFFFF;
	}
	else {
		result = regValue & 0xFFFF;
	}
	printf("read ADC input value channel: %d, value: %x\n", channel, result);

	return true;
}

// 设置DAC的输出值
bool writeDACOutputValue(HANDLE deviceHandle, int channel, ULONG value)
{
	// 通道号从1开始. value值低16位有效，低16位的最高位是符号位，符号位为1输出负值，为0输出正值
	
	MMIO_ACCESS regAccess;
	ULONG bytesReturned = 0;

	regAccess.Zone = 4;
	regAccess.RegisterOffset = (channel - 1) * 4;
	regAccess.RegisterValue = value;	
	if (!DeviceIoControl(
		deviceHandle,
		MMIO_CTL_WR_REG,
		&regAccess,
		sizeof(regAccess),
		NULL,
		0,
		&bytesReturned,
		NULL))
	{
		printf("MY_IOCTL_WR_REG failed, error = %u", GetLastError());
		return false;
	}
	printf("write DAC output value function channel: %d, register: %x, value: %x\n", channel, regAccess.RegisterOffset, regAccess.RegisterValue);

	return true;
}

// 设置DAC的输出值，方式2
bool writeDACOutputValueEx(HANDLE deviceHandle, int channel, ULONG value)
{
	UINT32 regAddrOffset = (channel - 1) * 4;
	*(PUINT32)((volatile UINT64)pMapMmioBase_Bar2 + regAddrOffset) = value;
	printf("write register: %x from user space MMIO, value: %x \n", regAddrOffset, value);

	return true;
}

// 中断处理函数
bool interupthandle(HANDLE deviceHandle)
{
	ULONG bytesReturned = 0;
	
	HANDLE eventHandle = CreateEvent(NULL, FALSE, FALSE, NULL);
	if (eventHandle == NULL)
	{
		printf("CreateEvent failed, error = %u", GetLastError());
		return false;
	}

	if (!DeviceIoControl(
		deviceHandle,
		IRQ_CTL_GIVE_EVENT_HANDLE,
		&eventHandle,
		sizeof(eventHandle),
		NULL,
		0,
		&bytesReturned,
		NULL
	))
	{
		printf("MY_IOCTL_GIVE_EVENT_HANDLE failed, error = %u",
			GetLastError());
		return false;
	}

	// 等待中断事件
	do
	{
		if (WaitForSingleObject(eventHandle, 1000) != WAIT_OBJECT_0)
		{
			break;
		}
		printf("get irq event. \n");
	} while (1);

}

int main(int argc, char* argv[])
{
	// 设置控制台输出为 UTF-8
	SetConsoleOutputCP(CP_UTF8);
	
	HANDLE deviceHandle = NULL;
	WCHAR devicePath[MAX_PATH];

	UINT32 nInResult = 0;			// 读取的数字量IO输入值
	UINT16 nADResult = 0;			// 读取的指定通道的AD采样值

	// 通过设备实例ID(InstanceID)，打开设备
	if (FindDeviceByInstanceId(L"PCI\\VEN_10EE&DEV_7014&SUBSYS_210110EE&REV_00\\4&13428119&0&00E8", devicePath, MAX_PATH))
	{
		deviceHandle = CreateFile(
			devicePath,
			GENERIC_READ | GENERIC_WRITE,
			0,
			NULL,
			OPEN_EXISTING,
			FILE_ATTRIBUTE_DEVICE,
			NULL
		);

		if (deviceHandle == INVALID_HANDLE_VALUE)
		{
			printf("CreateFile failed, error = %u",
				GetLastError());
		}

		printf("Open PciTestDevice successfully.\n");
	}

	// 通过设备位置，打开设备
	/*if (FindDeviceByLocation(L"PCI 总线 12、设备 0、功能 0", devicePath, MAX_PATH))
	{
		deviceHandle = CreateFile(
			devicePath,
			GENERIC_READ | GENERIC_WRITE,
			0,
			NULL,
			OPEN_EXISTING,
			FILE_ATTRIBUTE_DEVICE,
			NULL
		);

		if (deviceHandle == INVALID_HANDLE_VALUE)
		{
			printf("CreateFile failed, error = %u",
				GetLastError());
		}

		printf("Open PciTestDevice successfully.\n");
	}*/

	// 通过给定的路径打开设备
	/*deviceHandle = CreateFileW(
		g_DevicePath,
		GENERIC_READ | GENERIC_WRITE,
		0,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_DEVICE,
		NULL
	);
	if (deviceHandle == INVALID_HANDLE_VALUE)
	{
		printf("CreateFile failed, error = %u",
			GetLastError());
	}

	printf("Open PciTestDevice successfully.\n");*/

	// 测试点亮板卡上的LED指示灯
	//testLightLED(deviceHandle);

	// 通道的初始化配置，使用方法1，每次都通过IOCTL进入到内核态
	//initChannelConf_DIO(deviceHandle,0);	// 1输入 0输出
	//initChannelConf_ADC(deviceHandle);
	//initChannelConf_DAC(deviceHandle);

	// 读取数字输入通道
	//ReadDIOInputValue(deviceHandle, nInResult);

	// 控制数字输出通道
	//setDIOOutputValue(deviceHandle, 0);					// 1-16通道是输入，17-32是输出，这里全部输出0
	//setDIOOutputValue(deviceHandle, 0xFFFFFFFF);		// 这里全输出1
	//setDIOOutputValue(deviceHandle, 0);

	// 控制DAC通道输出
	//writeDACOutputValue(deviceHandle, 1, 0x3FFF);		// 控制第1个通道的输出,通道号从1开始。 低16位有效，低16位的最高位是符号位，符号位为1输出负值，为0输出正值

	// 读取ADC的输入
	//readADCValue(deviceHandle, 1, nADResult);



	// 将MMIO映射到用户地址空间
	MapMMIOToUserBase(deviceHandle);

	// 通道的初始化配置，方式2
	//initChannelConfEx_DAC(deviceHandle);
	//initChannelConfEx_ADC(deviceHandle);
	initChannelConfEx_DIO(deviceHandle, 0);

	// 控制DAC通道输出，方式2
	//writeDACOutputValueEx(deviceHandle, 1, 0x3FFF);

	// 读取ADC的输入，方式2
	//readADCValueEx(deviceHandle, 1, nADResult);

	// 控制数字输出通道
	setDIOOutputValueEx(deviceHandle, 0);
	setDIOOutputValueEx(deviceHandle, 0xFFFFFFFF);
	setDIOOutputValueEx(deviceHandle, 0);

	// 读取数字输入通道
	ReadDIOInputValueEx(deviceHandle, nInResult);

	// 解除MMIO到用户地址空间的映射
	UnmapMMIOToUserBase(deviceHandle);

	// 关闭设备文件
	if (deviceHandle)
	{
		CloseHandle(deviceHandle);
	}

	return 0;
}

// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单

// 入门使用技巧: 
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件，或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来，若要再次打开此项目，请转到“文件”>“打开”>“项目”并选择 .sln 文件
